package cn.edu.uestc.topo;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.*;
import java.util.*;

/**
 * 在此，我们对整体的拓扑生成逻辑进行一个简要的说明：
 * 1、总共有三个拓扑，分别对应于高中低三个级别。
 * 最低级别的拓扑仅有12个节点和16条链路，其主要用于单元测试（人工可查找）
 * 居中级别的拓扑有38个节点和近300条链路，其为Tencent DCI的核心拓扑
 * 最高级别的拓扑含60个节点和近1000条链路，其为压力测试的拓扑
 *
 * 2、由于输入数据的限制，三个拓扑信息的生成分别依据不同的方法实现。
 */

public class JsonGen {

    private static int initBw = 1000;
    private static int initDelay = 2048;
    private static int initHop = 1024;

    public static int getPriority(int prio, int type) {
        int p = 0;

        if (prio == 1 && type == 2) {
            // copper backup
            p = 1;
        } else if (prio == 1 && type == 1) {
            // copper main
            p = 4;
        } else if (prio == 2 && type == 2) {
            // iron backup
            p = 2;
        } else if (prio == 2 && type == 1) {
            // iron main
            p = 5;
        } else if (prio == 3 && type == 2) {
            // silver backup
            p = 3;
        } else if (prio == 3 && type == 1) {
            // silver main
            p = 6;
        } else if (prio == 4 && type == 2) {
            // gold backup
            p = 7;
        } else if (prio == 4 && type == 1) {
            // gold main
            p = 8;
        }

        return p;
    }

    public static void writeJsonObjectToFile(String fileName, JSONObject jsonObject) {

        System.out.println("Write JSON Object into File " + fileName + " ...");
        File file = new File(fileName);

        if (file.isFile() && file.exists()) {
            try {
                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
                bufferedWriter.write(jsonObject.toString(4));
                bufferedWriter.flush();
                bufferedWriter.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("Write JSON Object into File Completed.");
    }

    public static void generateHighResources() {

        String highTopoJsonFile = "C:/Resources/dcitopo/dcitopo_high/topo.json";
        String highLinkJsonFile = "C:/Resources/dcitopo/dcitopo_high/link.json";
        String highNodeJsonFile = "C:/Resources/dcitopo/dcitopo_high/node.json";
        String highTunnelJsonFile = "C:/Resources/dcitopo/dcitopo_high/tunnel.json";
        String highStatJsonFile = "C:/Resources/dcitopo/dcitopo_high/stat.json";

        String highNodeSrcFile = "C:/Resources/dcitopo/dcitopo_high/data/node.dat";
        String highLinkSrcFile = "C:/Resources/dcitopo/dcitopo_high/data/link.dat";

        List<Node> nodeList = new ArrayList<>();
        List<Link> linkList = new ArrayList<>();
        Topology topology = new Topology();

        File nodeFile = new File(highNodeSrcFile);
        List<String> nodeNameList = new ArrayList<>();
        List<String> nodeCityList = new ArrayList<>();

        try {
            String lineContent = null;
            BufferedReader nodeReader = new BufferedReader(new InputStreamReader(new FileInputStream(nodeFile), "UTF-8"));

            nodeReader.readLine();
            while ((lineContent = nodeReader.readLine()) != null) {
                /** Format:
                 * number node_name node_city
                 * 0      BJ1	    Beijing
                 */
                String[] strings = lineContent.split("\\s+");
                nodeNameList.add(strings[1]);
                nodeCityList.add(strings[2]);
            }

            nodeReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        File linkFile = new File(highLinkSrcFile);
        List<String> linkNameList = new ArrayList<>();
        List<Long> bwList = new ArrayList<>();
        List<Integer> metricList = new ArrayList<>();
        List<Integer> delayList = new ArrayList<>();

        try {
            String lineContent = null;
            BufferedReader linkReader = new BufferedReader(new InputStreamReader(new FileInputStream(linkFile), "UTF-8"));

            linkReader.readLine();
            while ((lineContent = linkReader.readLine()) != null) {
                // Format:
                // number link_name link_type    bandwidth(MB) metric delay
                // 0	  SZ1-SZ2   1            410000		   100	  20
                String[] strings = lineContent.split("\\s+");
                linkNameList.add(strings[1]);
                bwList.add(Long.parseLong(strings[3]) * 1000);
                metricList.add(Integer.parseInt(strings[4]));
                delayList.add(Integer.parseInt(strings[5]));
            }

            linkReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Topology region.
        int regionIndex = 1;
        UUID regionID = UUID.randomUUID();
        UUID topoId = UUID.randomUUID();
        topology.setId(topoId);

        Map<String, UUID> nodeName2IdMap = new HashMap<>();
        System.out.println("\n-------------------------Generate High Level Nodes and Links-------------------------");

        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        // Node Handle
        int nodeIndex = 0;
        while (nodeIndex < nodeNameList.size()) {
            Node node = new Node();

            UUID nodeId = UUID.randomUUID();
            node.setId(nodeId);

            String nodeName = nodeNameList.get(nodeIndex);
            node.setName(nodeName);

            String nodeCity = nodeCityList.get(nodeIndex);
            node.setCity(nodeCity);

            String mgtIP = "192.168." + regionIndex + "." + (nodeIndex + 1);
            node.setMgtIP(mgtIP);

            String loopbackIP = "192.168." + regionIndex + "." + (254 - nodeIndex);
            node.setLoopbackIP(loopbackIP);

            // Node status: active.
            node.setStatus(1);

            node.setInactiveReason(1);

            String serialNum = "TENCENT_BR_" + nodeName;
            node.setSerialNum(serialNum);

            String objectOId = "UESTCSCIEKB_" + nodeName + "_QPSK";
            node.setObjectOid(objectOId);

            node.setSoftVersion("v1.0.0-beta");

            // Vendor: unknown.
            node.setVendor(65535);

            node.setRegionId(regionID);

            UUID netconfCfgId = UUID.randomUUID();
            node.setNetconfCfgId(netconfCfgId);

            UUID snmpCfgId = UUID.randomUUID();
            node.setSnmpCfgId(snmpCfgId);

            SegmentRoutingCapability segmentRoutingCapability = new SegmentRoutingCapability();
            node.setSrCapability(segmentRoutingCapability);

            List<UUID> ports = new ArrayList<>();
            node.setPortList(ports);

            nodeList.add(node);
            nodeName2IdMap.put(nodeName, nodeId);

            nodeIndex++;
        }
        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        // Link Handle
        int linkIndex = 0;
        while (linkIndex < linkNameList.size()) {

            // Private Link
            Link priLink = new Link();
            try {
                UUID linkId = UUID.randomUUID();
                priLink.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                priLink.setName(linkName + "_PRIVATE");

                priLink.setType(1);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                priLink.setSrcNodeId(srcNodeId);

                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);
                priLink.setDstNodeId(dstNodeId);

                priLink.setSrcPortId(UUID.randomUUID());
                priLink.setDstPortId(UUID.randomUUID());

                priLink.setStatus(1);
                priLink.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                priLink.setMetric(metric);

                long bandwidth = bwList.get(linkIndex);
                priLink.setBandwidth(bandwidth);

                priLink.setReservableBandwidth(bandwidth);
                priLink.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                priLink.setDelay(delay);

                priLink.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            linkList.add(priLink);

            // Private back link
            Link priLinkBack = new Link();
            try {
                UUID linkId = UUID.randomUUID();
                priLinkBack.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                //priLinkBack.setName(linkName + "_PRIVATE");

                priLinkBack.setType(1);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);

                String linkBackName = dstNodeName + "-" + srcNodeName + "_PRIVATE";
                priLinkBack.setName(linkBackName);

                priLinkBack.setSrcNodeId(dstNodeId);
                priLinkBack.setDstNodeId(srcNodeId);

                priLinkBack.setSrcPortId(UUID.randomUUID());
                priLinkBack.setDstPortId(UUID.randomUUID());

                priLinkBack.setStatus(1);
                priLinkBack.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                priLinkBack.setMetric(metric);

                long bandwidth = bwList.get(linkIndex);
                priLinkBack.setBandwidth(bandwidth);

                priLinkBack.setReservableBandwidth(bandwidth);
                priLinkBack.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                priLinkBack.setDelay(delay);

                priLinkBack.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            linkList.add(priLinkBack);

            // Public Link
            Link pubLink = new Link();
            try {
                UUID linkId = UUID.randomUUID();
                pubLink.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                pubLink.setName(linkName + "_PUBLIC");

                pubLink.setType(2);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                pubLink.setSrcNodeId(srcNodeId);

                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);
                pubLink.setDstNodeId(dstNodeId);

                pubLink.setSrcPortId(UUID.randomUUID());
                pubLink.setDstPortId(UUID.randomUUID());

                pubLink.setStatus(1);
                pubLink.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                pubLink.setMetric(metric * 10);

                pubLink.setBandwidth(10000 * 1000);

                pubLink.setReservableBandwidth(10000 * 1000);
                pubLink.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                pubLink.setDelay(delay);

                pubLink.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            linkList.add(pubLink);

            Link pubLinkBack = new Link();
            try {
                UUID linkId = UUID.randomUUID();
                pubLinkBack.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                //pubLinkBack.setName(linkName + "_PUBLIC");

                pubLinkBack.setType(2);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);

                String linkBackName = dstNodeName + "-" + srcNodeName + "_PUBLIC";
                pubLinkBack.setName(linkBackName);

                pubLinkBack.setSrcNodeId(dstNodeId);
                pubLinkBack.setDstNodeId(srcNodeId);

                pubLinkBack.setSrcPortId(UUID.randomUUID());
                pubLinkBack.setDstPortId(UUID.randomUUID());

                pubLinkBack.setStatus(1);
                pubLinkBack.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                pubLinkBack.setMetric(metric * 10);

                pubLinkBack.setBandwidth(10000 * 1000);

                pubLinkBack.setReservableBandwidth(10000 * 1000);
                pubLinkBack.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                pubLinkBack.setDelay(delay);

                pubLinkBack.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            linkList.add(pubLinkBack);

            linkIndex++;
        }
        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        // Topology Handle and Json File Written.
        try {
            List<UUID> nodeIdList = new ArrayList<>();
            JSONArray nodeJsonArray = new JSONArray();
            for (Node node : nodeList) {
                nodeIdList.add(node.getId());
                nodeJsonArray.put(node.writeToJson());
            }
            JSONObject nodesJO = new JSONObject();
            nodesJO.put("topoNodes", nodeJsonArray);

            List<UUID> linkIdList = new ArrayList<>();
            JSONArray linkJsonArray = new JSONArray();
            for (Link link : linkList) {
                linkIdList.add(link.getId());
                linkJsonArray.put(link.writeToJson());
            }
            JSONObject linksJO = new JSONObject();
            linksJO.put("topoLinks", linkJsonArray);

            topology.setNodeList(nodeIdList);
            topology.setLinkList(linkIdList);

            writeJsonObjectToFile(highTopoJsonFile, topology.writeToJson());
            writeJsonObjectToFile(highNodeJsonFile, nodesJO);
            writeJsonObjectToFile(highLinkJsonFile, linksJO);

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("------------------------High-Level Topology Basic Information------------------------");
        System.out.println("Topo Node Count = " + nodeList.size());
        System.out.println("Topo Link Count = " + linkList.size());

        System.out.println("\n------------------Generate High-Level TunnelInfo List and Stat List------------------");

        List<TunnelInfo> tunnelInfoList = new ArrayList<>();
        List<Stat> statList = new ArrayList<>();

        int sIndex = 0;
        int dIndex = 0;
        for (sIndex = 0; sIndex < nodeList.size() - 1; sIndex++) {
            for (dIndex = sIndex + 1; dIndex < nodeList.size(); dIndex++) {
                Node srcNode = nodeList.get(sIndex);
                Node dstNode = nodeList.get(dIndex);

                if (srcNode.getCity().equals(dstNode.getCity())) {
                    continue;
                }

                for (int cnt = 1; cnt <= 4; cnt++) {
                    for (int prio = 1; prio <= 4; prio++) {
                        List<UUID> nullList = new ArrayList<>();
                        String passSequence = null;

                        TunnelInfo forwardTunnelInfo = new TunnelInfo();
                        UUID forwardTunnelId = UUID.randomUUID();
                        forwardTunnelInfo.setId(forwardTunnelId);
                        forwardTunnelInfo.setName("TF_" + srcNode.getName() + "-" + dstNode.getName());
                        forwardTunnelInfo.setNodeId(UUID.randomUUID());
                        forwardTunnelInfo.setPortId(UUID.randomUUID());
                        forwardTunnelInfo.setLinkId(UUID.randomUUID());
                        forwardTunnelInfo.setIp("192.168.45.11/24");
                        forwardTunnelInfo.setSrcIP(srcNode.getLoopbackIP());
                        forwardTunnelInfo.setDstIP(dstNode.getLoopbackIP());
                        forwardTunnelInfo.setPriority(prio);
                        forwardTunnelInfo.setBandwidthRequest(initBw);
                        forwardTunnelInfo.setDelayRequest(initDelay);
                        forwardTunnelInfo.setHopRequest(initHop);
                        forwardTunnelInfo.setAvoidLinkList(nullList);
                        forwardTunnelInfo.setAvoidNodeList(nullList);
                        forwardTunnelInfo.setPassLinkList(nullList);
                        forwardTunnelInfo.setPassNodeList(nullList);
                        forwardTunnelInfo.setPassSequence(passSequence);

                        Stat forwardStat = new Stat();
                        UUID forwardStatId = UUID.randomUUID();
                        forwardStat.setId(forwardStatId);
                        forwardStat.setTunnelId(forwardTunnelId);
                        forwardStat.setPriority(prio);
                        forwardStat.setSequenceNumber(0);
                        forwardStat.setMeterFlow(initBw);
                        forwardStat.setMeterDelay(100);
                        forwardStat.setMeterPacketLoss(0);

                        TunnelInfo inverseTunnelInfo = new TunnelInfo();
                        UUID inverseTunnelId = UUID.randomUUID();
                        inverseTunnelInfo.setId(inverseTunnelId);
                        inverseTunnelInfo.setName("TI_" + dstNode.getName() + "-" + srcNode.getName());
                        inverseTunnelInfo.setNodeId(UUID.randomUUID());
                        inverseTunnelInfo.setPortId(UUID.randomUUID());
                        inverseTunnelInfo.setLinkId(UUID.randomUUID());
                        inverseTunnelInfo.setIp("192.168.45.16/24");
                        inverseTunnelInfo.setSrcIP(dstNode.getLoopbackIP());
                        inverseTunnelInfo.setDstIP(srcNode.getLoopbackIP());
                        inverseTunnelInfo.setPriority(prio);
                        inverseTunnelInfo.setBandwidthRequest(initBw);
                        inverseTunnelInfo.setHopRequest(initHop);
                        inverseTunnelInfo.setDelayRequest(initDelay);
                        inverseTunnelInfo.setAvoidLinkList(nullList);
                        inverseTunnelInfo.setAvoidNodeList(nullList);
                        inverseTunnelInfo.setPassLinkList(nullList);
                        inverseTunnelInfo.setPassNodeList(nullList);
                        inverseTunnelInfo.setPassSequence(passSequence);

                        Stat inverseStat = new Stat();
                        UUID id = UUID.randomUUID();
                        inverseStat.setId(id);
                        inverseStat.setTunnelId(inverseTunnelId);
                        inverseStat.setPriority(prio);
                        inverseStat.setSequenceNumber(0);
                        inverseStat.setMeterDelay(100);
                        inverseStat.setMeterFlow(initBw);
                        inverseStat.setMeterPacketLoss(0);


                        tunnelInfoList.add(forwardTunnelInfo);
                        tunnelInfoList.add(inverseTunnelInfo);

                        statList.add(forwardStat);
                        statList.add(inverseStat);
                    }
                }
            }
        }

        try {
            JSONObject tunnelInfosJson = new JSONObject();
            JSONArray tunnelJsonArray = new JSONArray();
            for (TunnelInfo tunnelInfo : tunnelInfoList) {
                tunnelJsonArray.put(tunnelInfo.writeToJson());
            }
            tunnelInfosJson.put("tunnelInfos", tunnelJsonArray);

            writeJsonObjectToFile(highTunnelJsonFile, tunnelInfosJson);

            JSONObject statsJson = new JSONObject();
            JSONArray statJsonArray = new JSONArray();
            for (Stat stat : statList) {
                statJsonArray.put(stat.writeToJson());
            }
            statsJson.put("networkStats", statJsonArray);

            writeJsonObjectToFile(highStatJsonFile, statsJson);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("-----------------------High-Level TunnelInfo and Stat Basic Information-----------------------");
        System.out.println("TunnelInfo List Count = " + tunnelInfoList.size());
        System.out.println("Stat List Count = " + statList.size());
    }

    public static void generateMidResources() {

        String midTopoJsonFile = "C:/Resources/dcitopo/dcitopo_mid/topo.json";
        String midLinkJsonFile = "C:/Resources/dcitopo/dcitopo_mid/link.json";
        String midNodeJsonFile = "C:/Resources/dcitopo/dcitopo_mid/node.json";
        String midTunnelJsonFile = "C:/Resources/dcitopo/dcitopo_mid/tunnel.json";
        String midStatJsonFile = "C:/Resources/dcitopo/dcitopo_mid/stat.json";

        String midNodeSrcFile = "C:/Resources/dcitopo/dcitopo_mid/data/node.dat";
        String midLinkSrcFile = "C:/Resources/dcitopo/dcitopo_mid/data/link.dat";

        List<Node> nodeList = new ArrayList<>();
        List<Link> linkList = new ArrayList<>();
        Topology topology = new Topology();

        File nodeFile = new File(midNodeSrcFile);
        List<String> nodeNameList = new ArrayList<>();
        List<String> nodeCityList = new ArrayList<>();

        try {
            String lineContent = null;
            BufferedReader nodeReader = new BufferedReader(new InputStreamReader(new FileInputStream(nodeFile), "UTF-8"));

            nodeReader.readLine();
            while ((lineContent = nodeReader.readLine()) != null) {
                /** Format:
                 * number node_name node_city
                 * 0      BJ1	    Beijing
                 */
                String[] strings = lineContent.split("\\s+");
                nodeNameList.add(strings[1]);
                nodeCityList.add(strings[2]);
            }

            nodeReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        File linkFile = new File(midLinkSrcFile);
        List<String> linkNameList = new ArrayList<>();
        List<Long> bwList = new ArrayList<>();
        List<Integer> metricList = new ArrayList<>();
        List<Integer> delayList = new ArrayList<>();

        try {
            String lineContent = null;
            BufferedReader linkReader = new BufferedReader(new InputStreamReader(new FileInputStream(linkFile), "UTF-8"));

            linkReader.readLine();
            while ((lineContent = linkReader.readLine()) != null) {
                // Format:
                // number link_name link_type    bandwidth(MB) metric delay
                // 0	  SZ1-SZ2   1            410000		   100	  20
                String[] strings = lineContent.split("\\s+");
                linkNameList.add(strings[1]);
                bwList.add(Long.parseLong(strings[3]) * 1000);
                metricList.add(Integer.parseInt(strings[4]));
                delayList.add(Integer.parseInt(strings[5]));
            }

            linkReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Topology region.
        int regionIndex = 1;
        UUID regionID = UUID.randomUUID();
        UUID topoId = UUID.randomUUID();
        topology.setId(topoId);

        Map<String, UUID> nodeName2IdMap = new HashMap<>();
        System.out.println("\n-------------------------Generate Mid Level Nodes and Links-------------------------");

        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        // Node Handle
        int nodeIndex = 0;
        while (nodeIndex < nodeNameList.size()) {
            Node node = new Node();

            UUID nodeId = UUID.randomUUID();
            node.setId(nodeId);

            String nodeName = nodeNameList.get(nodeIndex);
            node.setName(nodeName);

            String nodeCity = nodeCityList.get(nodeIndex);
            node.setCity(nodeCity);

            String mgtIP = "192.168." + regionIndex + "." + (nodeIndex + 1);
            node.setMgtIP(mgtIP);

            String loopbackIP = "192.168." + regionIndex + "." + (254 - nodeIndex);
            node.setLoopbackIP(loopbackIP);

            // Node status: active.
            node.setStatus(1);

            node.setInactiveReason(1);

            String serialNum = "TENCENT_BR_" + nodeName;
            node.setSerialNum(serialNum);

            String objectOId = "UESTCSCIEKB_" + nodeName + "_QPSK";
            node.setObjectOid(objectOId);

            node.setSoftVersion("v1.0.0-beta");

            // Vendor: unknown.
            node.setVendor(65535);

            node.setRegionId(regionID);

            UUID netconfCfgId = UUID.randomUUID();
            node.setNetconfCfgId(netconfCfgId);

            UUID snmpCfgId = UUID.randomUUID();
            node.setSnmpCfgId(snmpCfgId);

            SegmentRoutingCapability segmentRoutingCapability = new SegmentRoutingCapability();
            node.setSrCapability(segmentRoutingCapability);

            List<UUID> ports = new ArrayList<>();
            node.setPortList(ports);

            nodeList.add(node);
            nodeName2IdMap.put(nodeName, nodeId);

            nodeIndex++;
        }
        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        // Link Handle
        int linkIndex = 0;
        while (linkIndex < linkNameList.size()) {

            // Private Link
            Link priLink = new Link();
            try {
                UUID linkId = UUID.randomUUID();
                priLink.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                priLink.setName(linkName + "_PRIVATE");

                priLink.setType(1);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                priLink.setSrcNodeId(srcNodeId);

                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);
                priLink.setDstNodeId(dstNodeId);

                priLink.setSrcPortId(UUID.randomUUID());
                priLink.setDstPortId(UUID.randomUUID());

                priLink.setStatus(1);
                priLink.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                priLink.setMetric(metric);

                long bandwidth = bwList.get(linkIndex);
                priLink.setBandwidth(bandwidth);

                priLink.setReservableBandwidth(bandwidth);
                priLink.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                priLink.setDelay(delay);

                priLink.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            linkList.add(priLink);

            // Public Link
            Link pubLink = new Link();
            try {
                UUID linkId = UUID.randomUUID();
                pubLink.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                pubLink.setName(linkName + "_PUBLIC");

                pubLink.setType(2);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                pubLink.setSrcNodeId(srcNodeId);

                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);
                pubLink.setDstNodeId(dstNodeId);

                pubLink.setSrcPortId(UUID.randomUUID());
                pubLink.setDstPortId(UUID.randomUUID());

                pubLink.setStatus(1);
                pubLink.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                pubLink.setMetric(metric * 10);

                pubLink.setBandwidth(10000 * 1000);

                pubLink.setReservableBandwidth(10000 * 1000);
                pubLink.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                pubLink.setDelay(delay);

                pubLink.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            linkList.add(pubLink);

            linkIndex++;
        }
        // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        // Topology Handle and Json File Written.
        try {
            List<UUID> nodeIdList = new ArrayList<>();
            JSONArray nodeJsonArray = new JSONArray();
            for (Node node : nodeList) {
                nodeIdList.add(node.getId());
                nodeJsonArray.put(node.writeToJson());
            }
            JSONObject nodesJO = new JSONObject();
            nodesJO.put("topoNodes", nodeJsonArray);

            List<UUID> linkIdList = new ArrayList<>();
            JSONArray linkJsonArray = new JSONArray();
            for (Link link : linkList) {
                linkIdList.add(link.getId());
                linkJsonArray.put(link.writeToJson());
            }
            JSONObject linksJO = new JSONObject();
            linksJO.put("topoLinks", linkJsonArray);

            topology.setNodeList(nodeIdList);
            topology.setLinkList(linkIdList);

            writeJsonObjectToFile(midTopoJsonFile, topology.writeToJson());
            writeJsonObjectToFile(midNodeJsonFile, nodesJO);
            writeJsonObjectToFile(midLinkJsonFile, linksJO);

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("------------------------Mid-Level Topology Basic Information------------------------");
        System.out.println("Topo Node Count = " + nodeList.size());
        System.out.println("Topo Link Count = " + linkList.size());

        System.out.println("\n------------------Generate Mid-Level TunnelInfo List and Stat List------------------");

        List<TunnelInfo> tunnelInfoList = new ArrayList<>();
        List<Stat> statList = new ArrayList<>();

        int sIndex = 0;
        int dIndex = 0;
        for (sIndex = 0; sIndex < nodeList.size() - 1; sIndex++) {
            for (dIndex = sIndex + 1; dIndex < nodeList.size(); dIndex++) {
                Node srcNode = nodeList.get(sIndex);
                Node dstNode = nodeList.get(dIndex);

                if (srcNode.getCity().equals(dstNode.getCity())) {
                    continue;
                }

                for (int cnt = 1; cnt <= 4; cnt++) {
                    for (int prio = 1; prio <= 4; prio++) {
                        List<UUID> nullList = new ArrayList<>();
                        String passSequence = null;

                        TunnelInfo forwardTunnelInfo = new TunnelInfo();
                        UUID forwardTunnelId = UUID.randomUUID();
                        forwardTunnelInfo.setId(forwardTunnelId);
                        forwardTunnelInfo.setName("TF_" + srcNode.getName() + "-" + dstNode.getName());
                        forwardTunnelInfo.setNodeId(UUID.randomUUID());
                        forwardTunnelInfo.setPortId(UUID.randomUUID());
                        forwardTunnelInfo.setLinkId(UUID.randomUUID());
                        forwardTunnelInfo.setIp("192.168.45.11/24");
                        forwardTunnelInfo.setSrcIP(srcNode.getLoopbackIP());
                        forwardTunnelInfo.setDstIP(dstNode.getLoopbackIP());
                        forwardTunnelInfo.setPriority(prio);
                        forwardTunnelInfo.setBandwidthRequest(initBw);
                        forwardTunnelInfo.setDelayRequest(initDelay);
                        forwardTunnelInfo.setHopRequest(initHop);
                        forwardTunnelInfo.setAvoidLinkList(nullList);
                        forwardTunnelInfo.setAvoidNodeList(nullList);
                        forwardTunnelInfo.setPassLinkList(nullList);
                        forwardTunnelInfo.setPassNodeList(nullList);
                        forwardTunnelInfo.setPassSequence(passSequence);

                        Stat forwardStat = new Stat();
                        UUID forwardStatId = UUID.randomUUID();
                        forwardStat.setId(forwardStatId);
                        forwardStat.setTunnelId(forwardTunnelId);
                        forwardStat.setPriority(prio);
                        forwardStat.setSequenceNumber(0);
                        forwardStat.setMeterFlow(initBw);
                        forwardStat.setMeterDelay(100);
                        forwardStat.setMeterPacketLoss(0);

                        TunnelInfo inverseTunnelInfo = new TunnelInfo();
                        UUID inverseTunnelId = UUID.randomUUID();
                        inverseTunnelInfo.setId(inverseTunnelId);
                        inverseTunnelInfo.setName("TI_" + dstNode.getName() + "-" + srcNode.getName());
                        inverseTunnelInfo.setNodeId(UUID.randomUUID());
                        inverseTunnelInfo.setPortId(UUID.randomUUID());
                        inverseTunnelInfo.setLinkId(UUID.randomUUID());
                        inverseTunnelInfo.setIp("192.168.45.16/24");
                        inverseTunnelInfo.setSrcIP(dstNode.getLoopbackIP());
                        inverseTunnelInfo.setDstIP(srcNode.getLoopbackIP());
                        inverseTunnelInfo.setPriority(prio);
                        inverseTunnelInfo.setBandwidthRequest(initBw);
                        inverseTunnelInfo.setHopRequest(initHop);
                        inverseTunnelInfo.setDelayRequest(initDelay);
                        inverseTunnelInfo.setAvoidLinkList(nullList);
                        inverseTunnelInfo.setAvoidNodeList(nullList);
                        inverseTunnelInfo.setPassLinkList(nullList);
                        inverseTunnelInfo.setPassNodeList(nullList);
                        inverseTunnelInfo.setPassSequence(passSequence);

                        Stat inverseStat = new Stat();
                        UUID id = UUID.randomUUID();
                        inverseStat.setId(id);
                        inverseStat.setTunnelId(inverseTunnelId);
                        inverseStat.setPriority(prio);
                        inverseStat.setSequenceNumber(0);
                        inverseStat.setMeterDelay(100);
                        inverseStat.setMeterFlow(initBw);
                        inverseStat.setMeterPacketLoss(0);


                        tunnelInfoList.add(forwardTunnelInfo);
                        tunnelInfoList.add(inverseTunnelInfo);

                        statList.add(forwardStat);
                        statList.add(inverseStat);
                    }
                }
            }
        }

        try {
            JSONObject tunnelInfosJson = new JSONObject();
            JSONArray tunnelJsonArray = new JSONArray();
            for (TunnelInfo tunnelInfo : tunnelInfoList) {
                tunnelJsonArray.put(tunnelInfo.writeToJson());
            }
            tunnelInfosJson.put("tunnelInfos", tunnelJsonArray);

            writeJsonObjectToFile(midTunnelJsonFile, tunnelInfosJson);

            JSONObject statsJson = new JSONObject();
            JSONArray statJsonArray = new JSONArray();
            for (Stat stat : statList) {
                statJsonArray.put(stat.writeToJson());
            }
            statsJson.put("networkStats", statJsonArray);

            writeJsonObjectToFile(midStatJsonFile, statsJson);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("-----------------------Mid-Level TunnelInfo and Stat Basic Information-----------------------");
        System.out.println("TunnelInfo List Count = " + tunnelInfoList.size());
        System.out.println("Stat List Count = " + statList.size());
    }


    public static void generateLowResources() {

        String lowTopoJsonFile = "C:/Resources/dcitopo/dcitopo_low/topo.json";
        String lowLinkJsonFile = "C:/Resources/dcitopo/dcitopo_low/link.json";
        String lowNodeJsonFile = "C:/Resources/dcitopo/dcitopo_low/node.json";

        String lowNodeSrcFile = "C:/Resources/dcitopo/dcitopo_low/data/node.dat";
        String lowLinkSrcFile = "C:/Resources/dcitopo/dcitopo_low/data/link.dat";

        List<Node> nodeList = new ArrayList<>();
        List<Link> linkList = new ArrayList<>();
        Topology topology = new Topology();

        File nodeFile = new File(lowNodeSrcFile);
        List<String> nodeNameList = new ArrayList<>();
        List<String> nodeCityList = new ArrayList<>();

        try {
            String lineContent = null;
            BufferedReader nodeReader = new BufferedReader(new InputStreamReader(new FileInputStream(nodeFile), "UTF-8"));

            nodeReader.readLine();
            while ((lineContent = nodeReader.readLine()) != null) {
                /** Format:
                 * number node_name node_city
                 * 0      BJ1	    Beijing
                 */
                String[] strings = lineContent.split("\\s+");
                nodeNameList.add(strings[1]);
                nodeCityList.add(strings[2]);
            }

            nodeReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        File linkFile = new File(lowLinkSrcFile);
        List<String> linkNameList = new ArrayList<>();
        List<Long> bwList = new ArrayList<>();
        List<Integer> metricList = new ArrayList<>();
        List<Integer> delayList = new ArrayList<>();

        try {
            String lineContent = null;
            BufferedReader linkReader = new BufferedReader(new InputStreamReader(new FileInputStream(linkFile), "UTF-8"));

            linkReader.readLine();
            while ((lineContent = linkReader.readLine()) != null) {
                // Format:
                // number link_name link_type    bandwidth(MB) metric delay
                // 0	  SZ1-SZ2   1            410000		   100	  20
                String[] strings = lineContent.split("\\s+");
                linkNameList.add(strings[1]);
                bwList.add(Long.parseLong(strings[3]));
                metricList.add(Integer.parseInt(strings[4]));
                delayList.add(Integer.parseInt(strings[5]));
            }

            linkReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Topology region.
        int regionIndex = 1;
        UUID regionID = UUID.randomUUID();
        UUID topoId = UUID.randomUUID();
        topology.setId(topoId);

        Map<String, UUID> nodeName2IdMap = new HashMap<>();
        System.out.println("\n---------------------Generate Test Nodes and Links---------------------");
        int nodeIndex = 0;

        while (nodeIndex < nodeNameList.size()) {
            Node node = new Node();

            UUID nodeId = UUID.randomUUID();
            node.setId(nodeId);

            String nodeName = nodeNameList.get(nodeIndex);
            node.setName(nodeName);

            String nodeCity = nodeCityList.get(nodeIndex);
            node.setCity(nodeCity);

            String mgtIP = "192.168." + regionIndex + "." + (nodeIndex + 1);
            node.setMgtIP(mgtIP);

            String loopbackIP = "192.168." + regionIndex + "." + (254 - nodeIndex);
            node.setLoopbackIP(loopbackIP);

            // Node status: active.
            node.setStatus(1);

            node.setInactiveReason(1);

            String serialNum = "TENCENT_BR_" + nodeName;
            node.setSerialNum(serialNum);

            String objectOId = "UESTCSCIEKB_" + nodeName + "_QPSK";
            node.setObjectOid(objectOId);

            node.setSoftVersion("v1.0.0-beta");

            // Vendor: unknown.
            node.setVendor(65535);

            node.setRegionId(regionID);

            UUID netconfCfgId = UUID.randomUUID();
            node.setNetconfCfgId(netconfCfgId);

            UUID snmpCfgId = UUID.randomUUID();
            node.setSnmpCfgId(snmpCfgId);

            SegmentRoutingCapability segmentRoutingCapability = new SegmentRoutingCapability();
            node.setSrCapability(segmentRoutingCapability);

            List<UUID> ports = new ArrayList<>();
            node.setPortList(ports);

            nodeList.add(node);
            nodeName2IdMap.put(nodeName, nodeId);

            nodeIndex++;
        }

        int linkIndex = 0;

        while (linkIndex < linkNameList.size()) {
            Link priLink = new Link();

            try {
                UUID linkId = UUID.randomUUID();
                priLink.setId(linkId);

                String linkName = linkNameList.get(linkIndex);
                priLink.setName(linkName);

                priLink.setType(1);

                String[] strings = linkName.split("-");

                String srcNodeName = strings[0];
                UUID srcNodeId = nodeName2IdMap.get(srcNodeName);
                priLink.setSrcNodeId(srcNodeId);

                String dstNodeName = strings[1];
                UUID dstNodeId = nodeName2IdMap.get(dstNodeName);
                priLink.setDstNodeId(dstNodeId);

                priLink.setSrcPortId(UUID.randomUUID());
                priLink.setDstPortId(UUID.randomUUID());

                priLink.setStatus(1);
                priLink.setAttributeFlags(0);

                int metric = metricList.get(linkIndex);
                priLink.setMetric(metric);

                long bandwidth = bwList.get(linkIndex);
                priLink.setBandwidth(bandwidth);

                priLink.setReservableBandwidth(bandwidth);
                priLink.setAllocatedBandwidth(0);

                int delay = delayList.get(linkIndex);
                priLink.setDelay(delay);

                priLink.setPolicy(0);
            } catch (Exception e) {
                e.printStackTrace();
            }

            linkList.add(priLink);
            linkIndex++;
        }

        try {
            List<UUID> nodeIdList = new ArrayList<>();
            JSONArray nodeJsonArray = new JSONArray();
            for (Node node : nodeList) {
                nodeIdList.add(node.getId());
                nodeJsonArray.put(node.writeToJson());
            }
            JSONObject nodesJO = new JSONObject();
            nodesJO.put("topoNodes", nodeJsonArray);

            List<UUID> linkIdList = new ArrayList<>();
            JSONArray linkJsonArray = new JSONArray();
            for (Link link : linkList) {
                linkIdList.add(link.getId());
                linkJsonArray.put(link.writeToJson());
            }
            JSONObject linksJO = new JSONObject();
            linksJO.put("topoLinks", linkJsonArray);

            topology.setNodeList(nodeIdList);
            topology.setLinkList(linkIdList);

            writeJsonObjectToFile(lowTopoJsonFile, topology.writeToJson());
            writeJsonObjectToFile(lowNodeJsonFile, nodesJO);
            writeJsonObjectToFile(lowLinkJsonFile, linksJO);

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("---------------------Test Topology Basic Information---------------------");
        System.out.println("Topo Node Count = " + nodeList.size());
        System.out.println("Topo Link Count = " + linkList.size());
    }
}
